using System;
using UnityEngine;
using System.Collections;

namespace Miao
{
    /// <summary>
    /// 协程的内部运行类
    /// </summary>
    internal class CoroutineInternal
    {
        /// <summary>
        /// 协程结束
        /// </summary>
        internal bool isFinish;
        /// <summary>
        /// 当前协程的子协程
        /// </summary>
        internal CoroutineInternal child;
        /// <summary>
        /// 当前协程的父协程
        /// </summary>
        internal CoroutineInternal parent;
        /// <summary>
        /// 外部句柄
        /// </summary>
        internal RunningCoroutine runningCoroutine;

        private readonly IEnumerator enumerator;

        private float duration;
        private float currentTime;
        private bool isTimeScale;

        private WaitType waitType;
        private Func<bool> nextFunction;
        private AsyncOperation asyncOperation;
        private CustomCoroutineWait customCoroutineWait;

        public CoroutineInternal(IEnumerator enumerator)
        {
            this.enumerator = enumerator;
        }

        /// <summary>
        /// 更新协程
        /// </summary>
        /// <returns>协程是否结束</returns>
        internal bool Update()
        {
            switch (waitType)
            {
                case WaitType.None: NoneWait(); break;
                case WaitType.Time: TimeWait(); break;
                case WaitType.Custom: CustomWait(); break;
                case WaitType.AsyncOperation: AsyncOperationWait(); break;
                case WaitType.Function: FunctionWait(); break;
            }
            return isFinish;
        }

        private void FunctionWait()
        {
            if (!nextFunction()) return;
            waitType = WaitType.None;
            NoneWait();//本帧立即执行
        }
        private void TimeWait()
        {
            float deltaTime = Time.deltaTime;
            //计算真实时间:使用 deltaTime * Time.timeScale而非 Time.unscaledDeltaTime
            //Time.unscaledDeltaTime在游戏刚启动的前几帧内,获取的时间会有问题 (偶发且未在运行时测试)
            currentTime += isTimeScale ? Time.deltaTime : deltaTime * Time.timeScale;
            if (currentTime < duration) return;
            waitType = WaitType.None;
            NoneWait();//本帧立即执行
        }
        /// <summary>
        /// 没有任何等待, 进行下一步
        /// </summary>
        private void NoneWait()
        {
            if (child && !child.isFinish) return;//如果子协程存在并且还没有结束, 那么要等子协程结束
            if (!enumerator.MoveNext())//迭代器运行结束
            {
                isFinish = true;
                return;
            }
            object currentObject = enumerator.Current;
            if (currentObject == null) return;
            if (currentObject is Wait wait)
            {
                currentTime = 0f;
                isTimeScale = true;
                duration = wait.duration;
                waitType = WaitType.Time;
                return;
            }
            if (currentObject is WaitRealtime waitRealtime)
            {
                currentTime = 0f;
                waitType = WaitType.Time;
                duration = waitRealtime.duration;
                return;
            }
            if (currentObject is IEnumerator childEnumerator)//拥有一个子协程
            {
                child = new CoroutineInternal(childEnumerator);
                child.parent = this;
                CoroutineStarter.AddInternal(child);
                return;
            }
            if (currentObject is RunningCoroutine runningCoroutine)//拥有一个子协程(RunningCoroutine)
            {
                child = runningCoroutine.coroutineInternal;
                child.parent = this;
            }
            if (currentObject is WaitUntilTrue waitUntilTrue)
            {
                waitType = WaitType.Function;
                nextFunction = waitUntilTrue.function;
                return;
            }
            if (currentObject is AsyncOperation asyncOperation)
            {
                this.asyncOperation = asyncOperation;
                waitType = WaitType.AsyncOperation;
                return;
            }
           
            if (currentObject is CustomCoroutineWait customCoroutineWait)
            {
                waitType = WaitType.Custom;
                this.customCoroutineWait = customCoroutineWait;
                return;
            }
        }
        private void CustomWait()
        {
            if (customCoroutineWait.Next()) waitType = WaitType.None;
        }
        private void AsyncOperationWait()
        {
            if (asyncOperation.isDone)
            {
                waitType = WaitType.None;
                NoneWait();//本帧立即执行
            }
        }

        public static implicit operator bool(CoroutineInternal self)
        {
            return self != null;
        }
    }
}